-- CONTENTS
-- usage: copy and find
-- [LOAD] Loading screen 
-- [MAIN] Main screen 
-- [GAME] Game screen
-- [GAMEKEYS] Game keys
-- [GAMEDRAW] Game draw
-- [GAMEUPDATE] Game update 
-- [HUD] Hud
-- [OVER] Overworld / Map
-- [TLOAD] Tilemap loading function

-- ** SCREENS ** --
----------------------
-- GLOBALS
----------------------
-- player stats
money = 9999

-- player skills
sword_acquired = true
double_jump_acquired = true
wall_jump_acquired = true
dash_acquired = true
power_glove_acquired = true
shield_acquired = true

-- stuff, imported from player_stuff.lua
--weapons = getWeapons()
--armors = getArmors()
--accessories = getAccessories()
--magics = getMagics()

-- current equipped stuff
current_weapon = nil
current_armor = nil
current_accessory = nil
left_binded_magic = nil
down_binded_magic = nil
right_binded_magic = nil

-- skenes
-- first time in the village
first_time_in_the_village = true

----------------------
-- Loading Screen
-- [LOAD]
----------------------
LoadingScreen = {}
LoadingScreen.__index = LoadingScreen

function LoadingScreen.new(current_screen, next_screen)
    local l = {}
    setmetatable(l, LoadingScreen)
    
    -- variables
    l.transition_time = 0.5
    l.state = "start"
    l.current_screen = current_screen -- reference to current screen
    l.next_screen = next_screen -- reference to next screen
    l.alpha = 0.0
    
    return l
end

-- load content
function LoadingScreen:loadContent()
    -- do nothing, nothing to load
end

-- update
function LoadingScreen:update()
    -- alpha step
    local alpha_step = ((global_delta_time / update_counter) / self.transition_time)
    
    -- start (debuggin purposes)
    if self.state == "start" then
        self.state = "fadeout"
        
        dprint("LoadingScreen", "Fadeout")
    end
    
    -- fade out
    if self.state == "fadeout" then
        if self.alpha >= 1.0 then
            self.state = "load"
        end
        
        self.alpha = self.alpha + alpha_step
        
        -- Fadeout sounds
        local a = self.alpha
        a = clamp(a, 0.0, 1.0)
        sound_manager:setMasterVolume(1.0 - a)
    end
    
    -- load
    if self.state == "load" then
        -- stop previous sounds and clear them
        sound_manager:stopAll()
        sound_manager:clear()
        dprint("LoadingScreen", "Previous musics stopped and cleared.")
    
        self.next_screen:loadContent()
        self.current_screen = self.next_screen
        self.state = "fadein"
        
        -- start also the new music
        sound_manager:play("background", true)
        
        dprint("LoadingScreen", "Loaded content of the next screen")
        dprint("LoadingScreen", "Fadein")
        dprint("LoadingScreen", "Background music started if any.")
    end
    
    -- fade in
    if self.state == "fadein" then
        if self.alpha <= 0.0 then
            self.state = "finished"
            
            dprint("LoadingScreen", "Loading transition finished")
        end
        
        self.alpha = self.alpha - alpha_step
        
        -- Fadein sounds
        local a = self.alpha
        a = clamp(a, 0.0, 1.0)
        sound_manager:setMasterVolume(1.0 - a)
    end
    
    -- finished, set new screen
    if self.state == "finished" then
        screen_manager:removeScreen(1) -- remove this loading screen
        screen_manager:pushScreen(self.next_screen)
        self.state = "undefined"
        
        sound_manager:setMasterVolume(1.0)
    end
    
    self.alpha = clamp(self.alpha, 0.0, 1.0)
end

-- draw
function LoadingScreen:draw()
    if self.current_screen ~= nil then
        self.current_screen:draw()
    end

	-- make it black
    love.graphics.setColor(0, 0, 0, self.alpha * 255)
    love.graphics.rectangle("fill", 0, 0, 512, 448)
    love.graphics.setColor(255, 255, 255, 255)
end

-- key press
function LoadingScreen:updateKeyPress(key)
end

-- key release
function LoadingScreen:updateKeyRelease(key)
end

----------------------
-- Main Menu
-- [MAIN]
----------------------
MainScreen = {}
MainScreen.__index = MainScreen

function MainScreen.new()
    local m = {}
    setmetatable(m, MainScreen)
    
    -- variables
    m.c_text = "PRESS ENTER"
    
    m.font = nil
    m.title_logo = nil
    m.pointer = nil
    m.pointer_x_offset = 0.0
    m.elapsed_time = 0.0
    m.index = 0
    
    return m
end

-- Load content
function MainScreen:loadContent()
    self.title_logo = Sprite.new()
    self.title_logo:loadImage("logo", "content/title-logo.png")
    self.pointer = Sprite.new()
    self.pointer:loadImage("pointer", "content/gui/hand-pointer.png")
end

-- Update
function MainScreen:update()
	self.elapsed_time = self.elapsed_time + global_delta_time
	if self.elapsed_time > (math.pi * 2) then self.elapsed_time = 0.0 end
	self.pointer_x_offset = math.sin(self.elapsed_time * 5) * 5
end

-- Draw
function MainScreen:draw()
	love.graphics.setFont(normal_font)
    self.title_logo:draw()

	local x = 90
    local y = 140
    drawString("New game", x, y)
    drawString("Continue", x, y + 15)
    self.pointer:draw(x - 20 + self.pointer_x_offset, y + (self.index * 15))
end

-- Update keypress
function MainScreen:updateKeyPress(key)
    if key == "down" or key == "s" then
        self.index = (self.index + 1) % 2
    elseif key == "up" or key == "w" then
        self.index = self.index - 1
        if self.index < 0 then
            self.index = 1
        end
    end
    
    if key == "return" then
        if self.index == 0 then
            screen_manager:loadScreen(OverWorldScreen.new())
        else
			-- nothing yet
        end
    end
    
    -- remove this
    --if key == "m" then
        -- screen_manager:pushScreen(IngameMenuScreen.new())
    --end
end

-- update keyrelease
function MainScreen:updateKeyRelease(key)
end

----------------------
-- OverWorld Screen
-- [OVER]
----------------------
OverWorldScreen = {}
OverWorldScreen.__index = OverWorldScreen

-- globals
map_current_location = 1

function OverWorldScreen.new()
	local o = {}
	setmetatable(o, OverWorldScreen)
	
	-- variables
	o.font = nil
	o.overworld = nil
	o.panel = nil
	o.marker = nil
	o.indicator = nil
	o.indicator_y_offset = 0.0
	o.elapsed_time = 0.0
	
	--o.current_location = 1
	o.previews = {}
	o.locations = {}
	o.names = {}
	o.unlocked = {}
	
	return o
end

-- load content
function OverWorldScreen:loadContent()
	-- sprites
	self.overworld = Sprite.new()
	self.overworld:loadImage("overworld", "content/world/overworld.png")
	self.panel = Sprite.new()
	self.panel:loadImage("panel", "content/world/overworld-panel.png")
	self.marker = Sprite.new()
	self.marker:loadImage("marker", "content/world/overworld-marker.png")
	self.indicator = Sprite.new()
	self.indicator:loadImage("indicator", "content/world/overworld-indicator.png")
	
	-- set locations (512 x 448)
	table.insert(self.locations, Point.new(82, 152))
	table.insert(self.locations, Point.new(100, 140))
	table.insert(self.locations, Point.new(90, 130))
    
	-- set names for locations and state
	local pre = Sprite.new()
	pre:loadImage("valhalla-pre", "content/world/overworld-valhalla-preview.png")
	table.insert(self.names, "Barham Village")
	table.insert(self.previews, pre)
	table.insert(self.unlocked, true)
	
	table.insert(self.names, "Sacred Dungeon")
	table.insert(self.unlocked, true)
	
	table.insert(self.names, "Forbidden Mountain")
	table.insert(self.unlocked, true)

	-- create font
    -- love.graphics.setFont("content/fonts/Chalkduster.ttf", 13)
end

-- draw normal map is 256 x 224
function OverWorldScreen:draw()
	love.graphics.setFont(small_font)
	
	-- draw overworld
	self.overworld:draw()
		
	-- draw panel
	self.panel:draw(133, 20)
	self.previews[1]:draw(134, 20) -- 1 -> map_current_location
	
	-- draw markers
	for i = 1, #self.locations do
		if self.unlocked[i] then
			-- marker dot
			self.marker:draw(self.locations[i].x, self.locations[i].y)
			
			-- some offsets for certain labels
			local x_offset = 4
			local y_offset = 4
			
			if i == 3 then -- forbidden mountain
				x_offset = - 30
				y_offset = - 12
			end
			
			-- draw label
            local xs = self.locations[i].x + x_offset
            local ys = self.locations[i].y + y_offset
			drawString(self.names[i], xs, ys)
		end
	end
	
	-- draw indicator
	self.indicator:draw(self.locations[map_current_location].x, 
		self.locations[map_current_location].y + self.indicator_y_offset - self.indicator:getHeight())
end

-- update
function OverWorldScreen:update()
	self.elapsed_time = self.elapsed_time + global_delta_time
	if self.elapsed_time > (math.pi * 2) then self.elapsed_time = 0.0 end -- prevent infinity growth
	self.indicator_y_offset = math.sin(self.elapsed_time * 5) * 3
end

-- key press
function OverWorldScreen:updateKeyPress(key)
	-- right
	if self:right(key) then
	end
	
	-- left
	if self:left(key) then
	end
	
	-- up
	if self:up(key) then
		if map_current_location == 1 and self.unlocked[2] then map_current_location = 2
		elseif map_current_location == 2 and self.unlocked[3] then map_current_location = 3 end
	end
	
	-- down
	if self:down(key) then
		if map_current_location == 2 then map_current_location = 1
		elseif map_current_location == 3 then map_current_location = 2 end
	end

	-- enter/return
	if key == "return" then
		if map_current_location == 1 then
            -- load next place
			screen_manager:loadScreen(GameScreen.new("barham-village"))
		end
        
        if map_current_location == 2 then
            -- load next place
			screen_manager:loadScreen(GameScreen.new("sacred-dungeon"))
		end
	end
end

-- key release
function OverWorldScreen:updateKeyRelease(key)
end

-- press left helper
function OverWorldScreen:left(key)
	if key == "a" or key == "left" then
		return true
	else
		return false
	end
end

-- press right helper
function OverWorldScreen:right(key)
	if key == "d" or key == "right" then
		return true
	else
		return false
	end
end

-- press up helper
function OverWorldScreen:up(key)
	if key == "w" or key == "up" then
		return true
	else
		return false
	end
end

-- press down helper
function OverWorldScreen:down(key)
	if key == "s" or key == "down" then
		return true
	else
		return false
	end
end

----------------------
-- Game screen
-- [GAME]
----------------------
GameScreen = {}
GameScreen.__index = GameScreen

function GameScreen.new(place_to_load)
    local g = {}
    setmetatable(g, GameScreen)
    
    -- constans
    g.INTERACT_NONE = 0
    g.INTERACT_NPC = 1
    
    -- variables: tilemap, player, enemies and so forth
    g.current_place = place_to_load
    g.camera = nil
    g.player = nil
    g.tilemap = nil
    g.backgrounds = nil
    g.frontgrounds = nil
    g.containers = {} -- object containers, enemies and all and also clamp areas
    g.current_container = nil
    g.spawns = {} -- spawn points for new map or world map
    g.collision_mask = nil
    g.interaction = g.INTERACT_NONE
    
    -- g.sleep_time = 0.0
    -- font
    --g.font = nil
    
    -- hud
    g.hud_back = nil
    g.hud_images = nil
    g.coin_anim = nil
    g.font = nil
    
    return g
end

-- load content
function GameScreen:loadContent()
	-- set camera
    self.camera = Camera.new(global_width, global_height)
    
    -- create hero
    self.player = Player.new()
    
    -- create hud
    self.hud_back = Sprite.new()
    self.hud_back:loadImage("back", "content/hud/hearts-background.png")
    self.hud_images = Sprite.new()
    self.hud_images:loadImage("button-layout", "content/hud/button-icons.png")
    self.hud_images:loadImage("sword-icon", "content/hud/sword.png")
    self.hud_images:loadImage("empty-heart-icon", "content/hud/empty-heart.png")
    self.hud_images:loadImage("full-heart-icon", "content/hud/full-heart.png")
    self.hud_images:loadImage("speech-bubble-icon", "content/hud/bubble.png")
    
    self.coin_anim = Sprite.new()
    self.coin_anim:loadImage("coin-icon", "content/hud/coin.png")
    self.coin_anim:addAnimation("coin", "coin-icon", 16, 16, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 0.04, true)
    
    -- create font
    --self.font = love.graphics.newFont("content/fonts/visitor2.ttf", 24)
    --love.graphics.setFont(self.font)
    
    -- load corresponding tilemap
    local player_h = self.player.bounding_box.height
    local player_w = self.player.bounding_box.width
    local ppos = nil
    
    -- load tilemap and all its stuff
    self.tilemap, 
    self.backgrounds, 
    self.frontgrounds, 
    self.containers, 
    self.spawns,
    ppos = loadTilemapFromFile("content/maps/" .. self.current_place .. ".tmx")
    
    if self.current_place == "barham-village" then
        if first_time_in_the_village then
            self.player.x = ppos[1].x            
            self.player.y = ppos[1].y - player_h
            first_time_in_the_village = false
        else
            self.player.x = ppos[2].x            
            self.player.y = ppos[2].y - player_h
            self.player.sprite:flip(true, false)
        end
        
        -- load background music
        sound_manager:load("background", "content/music/barham-village.mp3")
    end
    
    if self.current_place == "sacred-dungeon" then
        self.player.x = ppos[1].x            
        self.player.y = ppos[1].y - player_h
    end
    
    -- Set collisions
    self.collision_mask = love.image.newImageData("content/collisions/" .. self.current_place .. ".png")
    
    -- correct camera
    self.camera:instantLockToTarget(self.player.x, self.player.y)
    self:lockAndClampCamera();
end

-- update
-- [GAMEUPDATE]
function GameScreen:update()
	-- PLAYER, COLLISIONS and OTHER INTERACTIONS
	self.player:updateKeyDown()
    
	-- tilemap update
    self.tilemap:update(self.camera)
    
    -- update back/frontgrounds
    self:updateBackgrounds()
    self:updateFrontgrounds()
    
    -- update hud
    self.coin_anim:update()

	-- update container
	self:updateCurrentContainer()
	
	-- Collision Detection
	-- update player
	self.player:update()
    
    -- For double jump when droppgin down from ledge
	if not ground and not self.player.jump and 
        self.player.vel_y > 0.0 and self.player.jump_count == 0 then
        self.player.jump_count = 1
	end
	
    -- clamp player to inside to tilemap before checking collisions
    if self.player.x < 2 then self.player.x = 2 end
    if self.player.x > self.tilemap:getWidthInPixels() - self.player.bounding_box.width - 2 then
        self.player.x = self.tilemap:getWidthInPixels() - self.player.bounding_box.width - 2
    end
    if self.player.y < 2 then self.player.y = 2 end
    if self.player.y > self.tilemap:getHeightInPixels() - self.player.bounding_box.height - 2 then
        self.player.y = self.tilemap:getHeightInPixels() - self.player.bounding_box.height - 2
    end
	
	-- PIXEL-PERFECT
	-- temp variables for collision detection
	self.player.wall = false
	self.player:updateBoundingBox()
	local bb = self.player.bounding_box
	
	-- horizontal
	-- check collisions, the three bottom points
	local r, g, b, a = 0, 0, 0, 0
	local walloffset = 2
	local ypos = 0
	
	-- default
    -- going right
    local yfix = 5 -- change this bigger if stuck on the ground/ceil
	if self.player.vel_x > 0.0 then
		for y = bb:top() + yfix, bb:bottom() - yfix do	
			-- check collision
			r, g, b, a = self.collision_mask:getPixel(bb:right(), y)
			-- diagonal, do nothing
			if r == 255 and g == 0 and b == 255 then a = 0 break end		
			if a ~= 0 then
				self.player:setWallState()
				self.player.x = (math.floor(bb:right() / 32) * 32) - bb.width
				break
			end
		end
    -- going left
	elseif self.player.vel_x < 0.0 then
		for y = bb:top() + yfix, bb:bottom() - yfix do
			-- check collision
			r, g, b, a = self.collision_mask:getPixel(bb:left(), y)
			-- diagonal, do nothing
			if r == 255 and g == 0 and b == 255 then a = 0 break end
			if a ~= 0 then 
				self.player:setWallState()
				self.player.x = (math.floor(bb:right() / 32) * 32)
				break
			end
		end
	end
	
	-- update
	self.player:updateBoundingBox()
	bb = self.player.bounding_box
	
    -- going up
    local xfix = 2 -- change bigger if player stuck on the wall
	if self.player.vel_y < 0.0 then
		self.player.ceil = false
		for x = bb:left() + xfix, bb:right() - xfix do
			r, g, b, a = self.collision_mask:getPixel(x, bb:top())
			if a ~= 0 then
				self.player:setCeilState()
				self.player.y = (math.floor(bb:top() / 32) * 32) + 32
				break 
			end
		end
    -- idle or going down
	elseif self.player.vel_y >= 0.0 then
		self.player.on_ground = false
		for x = bb:left() + xfix, bb:right() - xfix do
			r, g, b, a = self.collision_mask:getPixel(x, math.floor(bb:bottom())) -- math.floor fixes wrong position bug
			if r == 0 and g == 0 and b == 0 and a ~= 0 then
				--if a ~= 0 then 
					self.player:setGroundState()
					self.player.y = (math.floor(bb:bottom() / 32) * 32) - bb.height
					break 
				--end
			-- diagonal
			elseif r == 255 and g == 0 and b == 255 and a ~= 0 then
				self.player:setGroundState()
				
				-- fix position
				local inside = true
				local y = bb:bottom()
				local tr1, tg1, tb1, ta1 = nil
				local count = 0
				
				while inside do
					count = count + 1
					if count >= 100 then break end
					tr1, tg1, tb1, ta1 = self.collision_mask:getPixel(bb:centerX(), y)
					if ta1 == 0 then inside = false else y = y - 1 end
				end
				
				-- apply
                local offset = 2
                if self.player.dash then offset = 1 end -- fix bug when dashin down along the diagonal collision
				self.player.y = y - bb.height + offset
				break
			end
		end
	end
	
	-- update box
	self.player:updateBoundingBox()
	
	-- Update interaction with enemies
	self:updateInteractionWithObjects()
    
    -- Update spawn points
    self:checkSpawnPoints()
    
    -- CAMERA and CLAMPING
    -- camera, lock player to center.
    self:lockAndClampCamera()
end

-- Locks camera to player and clamps the area.
function GameScreen:lockAndClampCamera()
    -- update
    self.camera:update()
    
    -- lock camera
    self.camera:lockToTarget(self.player.x, self.player.y)
    
    -- determine which clamp
    if self.containers ~= nil then
        if self.current_container == nil then
            -- first time
            self.current_container = self:getCurrentContainer(self.player.x, self.player.y)
        else
            -- only if player is not in current area
            if pointRectIntersect(self.player.x, self.player.y, self.current_container.clamp_area) == 0 then
                local temp = self:getCurrentContainer(self.player.x, self.player.y)                
                -- init clamp and objects
                if self.current_container ~= nil then
                    if temp ~= self.current_container then
                        local previous_container_id = self.current_container.id
                        self.current_container = temp
                        self.current_container:init(previous_container_id)
                        dprint("Game Screen", "Objects initialized")
                    end
                end
            end
        end
        
        -- clamp
        local clamp_area = nil
        if self.current_container ~= nil then
            clamp_area = self.current_container.clamp_area
        end
        if clamp_area ~= nil then
            self.camera:clampArea(clamp_area:left(),
                                  clamp_area:top(),
                                  clamp_area.width,
                                  clamp_area.height)
        end
    else
        self.camera:clampArea(0, 
                              0,
                              self.tilemap.width * self.tilemap.tilewidth,
                              self.tilemap.height * self.tilemap.tileheight)
    end
end

-- check spawns
function GameScreen:checkSpawnPoints()
    if self.spawns == nil then return end
    if #self.spawns == 0 then return end
    
    -- if village
    for i = 1, #self.spawns do
        if intersect(self.player.bounding_box, self.spawns[i]) then
            screen_manager:loadScreen(OverWorldScreen.new())
            dprint("Game Screen", "Spawn point " .. i)
        end
    end
end

-- draw hud
-- [HUD]
function GameScreen:drawHud()
    -- set font
    love.graphics.setFont(normal_font)

	-- coin
	self.coin_anim:draw(5, 200)
	
	-- button layout
	self.hud_images:setImage("button-layout")
	self.hud_images:draw(190, 5)
	
	-- draw hearts
    self.hud_back:draw(35, 5)
	self.hud_images:setImage("full-heart-icon")
	--local y_offset = 0
	local h = self.hud_images:getHeight()
	local w = self.hud_images:getWidth()
	local changed = false
	
	-- draw hearts, first full then empty
    local p = self.player
	for i = 0, (p.max_hearts - 1) do
		if (p.hearts - 1) < i and not changed then
			self.hud_images:setImage("empty-heart-icon")
			changed = true
		end
		
		-- draw
        self.hud_images:draw(38 + (i * 10), 6)
	end
    
    -- draw texts
    drawString(p.hp, 20, 2, 15, "right")
    drawString(money, 50, 200, 25, "right")
	
	-- draw sword
    if self.interaction == self.INTERACT_NONE then
        if not self.player:canHit() and sword_acquired and not self.player.hit_control_lock then
            self.hud_images:setImage("sword-icon")
            self.hud_images:draw(188, 4)
        end
    elseif self.interaction == self.INTERACT_NPC then
        self.hud_images:setImage("speech-bubble-icon")
        self.hud_images:draw(188, 4)
    end
end

-- draw
-- [GAMEDRAW]
function GameScreen:draw()
    -- fill color
    if self.current_place == "barham-village" then
        love.graphics.setColor(121, 185, 255, 255) -- blue
        love.graphics.rectangle("fill", 0, 0, global_width, global_height);
        love.graphics.setColor(255, 255, 255, 255) -- white
    end
    
    -- backgrounds
    self:drawBackgrounds(self.camera)
    
    -- backgrounds
    self.tilemap:drawLayer("background layer", self.camera)
    
    -- objects
    self:drawCurrentContainer(self.camera)
    self.player:draw(self.camera)
    
    -- front layers
    self.tilemap:drawLayer("front layer", self.camera)
    
    -- draw effects of objects
    self.player:drawEffects(self.camera)
    self:drawCurrentContainerEffects(self.camera)
    
    -- front grounds
    self:drawFrontgrounds(self.camera)

	-- hud
	self:drawHud()
end

-- draw backgrounds
function GameScreen:drawBackgrounds(camera)
	if self.backgrounds ~= nil then
		if #self.backgrounds ~= 0 then
			for i = 1, #self.backgrounds do
				self.backgrounds[i]:draw(camera)
			end
		end
	end
end

-- update backgrounds
function GameScreen:updateBackgrounds()
	if self.backgrounds ~= nil then
		if #self.backgrounds ~= 0 then
			for i = 1, #self.backgrounds do
				self.backgrounds[i]:update(self.camera, self.player.vel_x, 
                    self.player.vel_y, self.current_container.id)
			end
		end
	end
end

-- draw frontgrounds
function GameScreen:drawFrontgrounds(camera)
	if self.frontgrounds ~= nil then
		if #self.frontgrounds ~= 0 then
			for i = 1, #self.frontgrounds do
				self.frontgrounds[i]:draw(camera)
			end
		end
	end
end

-- update frontgrounds
function GameScreen:updateFrontgrounds()
	if self.frontgrounds ~= nil then
		if #self.frontgrounds ~= 0 then
			for i = 1, #self.frontgrounds do
				self.frontgrounds[i]:update(self.camera, self.player.vel_x, 
                    self.player.vel_y, self.current_container.id)
			end
		end
	end
end


-- update player and objects interaction
-- boxes, enemies, npcs, doors and etc.
function GameScreen:updateInteractionWithObjects()
    -- Joitain oletusarvoja ennen tsekkaamista.
    self.interaction = self.INTERACT_NONE
    self.player.interact = false

    -- Kaydaan lapi containerit.
    if self.containers ~= nil then
        local container = self.current_container
        
        if container ~= nil then
            for i = 1, container:getSize() do
                local obj = container:getObject(i)
                
                -- Weapon interaction
                if obj.c_type == "block" or obj.c_type == "enemy" then
                    if obj.state == "alive" then
                        if obj:canTakeHit() then
                            if intersect(self.player.weapon_hit_box, obj.bounding_box) and 
                               self.player:canHit() then
                               
                                -- player hit and obj takes hit
                                self.player:hit()
                                local dead = obj:takeHit(1, self.player.weapon_hit_box:centerX(), 
                                        self.player.weapon_hit_box:top())
                                        
                                -- enemy is dead, then shake camera a little
                                if dead then
                                    self.camera.quake_effect:play(0.4, 0.02, 2.0)
                                end
                            end
                        end
                    end
                end
                
                -- Doors
				-- TODO korjaa seinä bugi
                if obj.c_type == "door" then
                    if obj.state ~= "opened" then
                        -- normal collisions
                        local is_collided = obj:isCollided(self.player)
                        if is_collided then
                            if obj:shouldApplyResponse() then
                                -- move player, nice little collision response
                                if self.player.vel_x > 0 then
                                    self.player.x = obj.x - self.player.bounding_box.width
                                    self.player.vel_x = 0.0
                                    self.player:setWallState()
                                elseif self.player.vel_x < 0 then
                                    self.player.x = obj.x + 64
                                    self.player.vel_x = 0.0
                                    self.player:setWallState()
                                end
                            end
                            
                            obj:activateDoor()
                        end
                    end
                end
                
                -- Blocks
                if obj.c_type == "block" then
                    if obj.state == "alive" then
                        -- normal collisions
                        local is_collided = obj:isCollided(self.player)
                        if is_collided then
                            -- move player, nice little collision response
                            if self.player.vel_x > 0 then
                                self.player.x = obj.x - self.player.bounding_box.width
                                self.player.vel_x = 0.0
                            elseif self.player.vel_x < 0 then
                                self.player.x = (obj.x + obj.w)
                                self.player.vel_x = 0.0
                            end
                        end
                    end
                end
                
                -- NPCs
                if obj.c_type == "tutorial npcs" then
                    local is_collided = obj:isCollided(self.player)
                    if is_collided then
                        -- Pelaaja ei voi lyoda vaan
                        -- puhuminen npc hahmojen kanssa asetetaan mahdolliseksi.
                        self.player.interact = true
                        self.interaction = self.INTERACT_NPC
                    end
                end
                
                -- Birds
                if obj.c_type == "birds" then
                    obj:activateBirds(self.player)
                end
                
                -- Enemies
                if obj.c_type == "enemy" then
                    if obj.state == "alive" then
                        -- update enemy AI
                        obj:updateAI(self.player)
                    
                        -- Player takes hit against enemies
                        if not self.player:isShieldOn() then
                            if self.player:canTakeHit() then
                                if intersect(self.player.bounding_box, obj.bounding_box) then
                                    -- enemy hits and player takes hit
                                    obj:hit()
                                    local game_over = self.player:takeHit(obj:getDamage())
                                    if game_over then
                                        -- TODO GAME OVER
                                        dprint("GameScreen", "GameOver")
                                    end
                                end
                            end
                        -- Against shield
                        else
                            -- TODO
                            if not self.player.shield_hit then
                                local m = Rectangle.new(self.player.bounding_box.x - 10,
                                                        self.player.bounding_box.y,
                                                        self.player.bounding_box.width + 20,
                                                        self.player.bounding_box.height)
                                if intersect(m, obj.bounding_box) then
                                    local side = 0 -- if enemy is on the left
                                    -- if on the right
                                    if self.player.bounding_box:centerX() < obj.bounding_box:centerX() then
                                        side = 1
                                    end
                                    
                                    obj:takeShieldHit()
                                    self.player:takeShieldHit(side)
                                end
                            end
                        end
                    end
                end
            end
        end
    end
end

-- update current containers
function GameScreen:updateCurrentContainer()
    if self.containers ~= nil then
        if self.current_container ~= nil then
            self.current_container:update()
        end
    end
end

-- draw current container
function GameScreen:drawCurrentContainer(camera)
    if self.containers ~= nil then
        if self.current_container ~= nil then
            self.current_container:draw(camera)
        end
    end
end

-- draw current container effects
function GameScreen:drawCurrentContainerEffects(camera)
    if self.containers ~= nil then
        if self.current_container ~= nil then
            self.current_container:drawEffects(camera)
        end
    end
end

-- Check and return current clamp
function GameScreen:getCurrentContainer(x, y)
	if #self.containers ~= 0 then
		for i = 1, #self.containers do
			if pointRectIntersect(x, y, self.containers[i].clamp_area) == 1 then
                dprint("GameScreen", "Container " .. i)
				return self.containers[i]
			end
		end
	end
	
	return self.current_container
end

-- key press
-- [GAMEKEYS]
function GameScreen:updateKeyPress(key)
    self.player:updateKeyPress(key)
    
    if key == "left" then
        if self.interaction == self.INTERACT_NPC then
            -- talk villagers
            if self.current_place == "barham-village" then
            	local obj = self.containers[1]:getFirstObjectByType("tutorial npcs")
                if obj ~= nil then
                	local dialogActive = obj:activateDialog()
                end
            end
        end
    end
    
	-- Menu
    if key == "return" then
		-- TODO: Uusi Menu
        --screen_manager:pushScreen(IngameMenuScreen.new())
    end
    
    -- Debug commands
    if global_debug then
        if key == "1" then
            sword_acquired = not sword_acquired
        end
        if key == "2" then
            double_jump_acquired = not double_jump_acquired
            if not double_jump_acquired then wall_jump_acquired = false end
        end
        if key == "3" then
            wall_jump_acquired = not wall_jump_acquired
        end
        if key == "4" then
            dash_acquired = not dash_acquired
        end
        
        if key == "i" then
            self.current_container:init()
            dprint("GameScreen", "Objects of the current container reinitialized")
        end
    end
end

-- key release
function GameScreen:updateKeyRelease(key)
end

-----------------
-- [TLOAD]
-- Tilemap function loading from external file
-----------------
function findpattern(str, pattern, start)
    local find = string.find(str, pattern, start)
    if find ~= nil then
        return string.sub(str, find)
    else
        return nil
    end
end

function loadTilemapFromFile(file_url)
	-- load file
	local file = love.filesystem.newFile(file_url)
	local success = file:open("r")
	
	if not success then
		dprint("loadTilemapFromFile", file_url .. " not found.")
		return
	else
		dprint("loadTilemapFromFile", file_url .. " loaded succesfully!")
	end
    
    -- the whole text file in single string variable
    local text = ""
    
    -- iterate lines and add to them into string
	for element in file:lines() do
		-- remove unncessary tags
		element = string.gsub(element, "<?(.+)?>", "") -- remove xml description
		
		-- trim white spaces from beginning
		element = string.gsub(element, "^[%s]+", "")
        
        -- add lines to string variable
        text = text .. element
	end
	
	-- close the file
	file:close()
    
    -- now parse stuff out from the single string.
    -- parse map props
    local parsed_text = collect(text)
    local xml_top = parsed_text[1]
    
    -- create map
    local w = xml_top.xarg["width"]
    local h = xml_top.xarg["height"]
    local tw = xml_top.xarg["tilewidth"]
    local th = xml_top.xarg["tileheight"]
    
    local tilemap = Tilemap.new(tonumber(w), tonumber(h), tonumber(tw), tonumber(th))
    local tiles = {} -- temp variable for constructin tiles to data (sequence and interval changes)
    local backgrounds = {}
    local frontgrounds = {}
    local containers = {}
    local spawns = {}
    local ppos = {PlayerSpawn.new(20, 20)}
    
    -- temps
    local c1 = nil
    local c2 = nil
    local c3 = nil
    for i = 1, #xml_top do
        -- top tileset, layer and objectgroup
        c1 = xml_top[i]
        
        -- set tileset
        if c1.label == "tileset" then
            -- We don't care about the collisions tileset, so only the actual tilesheet
            if c1.xarg["name"] == "tilesheet" then
                -- create tileset
                local src = c1[1].xarg["source"]
                local tws = c1.xarg["tilewidth"]
                local ths = c1.xarg["tileheight"]
                src = string.gsub(src, "[a-z.]+/", "")
                tilemap:setTileset("content/world/" .. src, tonumber(tws), tonumber(ths))
                
                -- build animated tiles, they are used when creating map tiles (data)
                for k = 2, #c1 do
                    c2 = c1[k] -- <tile>
                    c3 = c2[1] -- <properties>
                    
                    -- <propery>
                    local interval = nil
                    local sequence = nil
                    local tseq     = {}
                    for m = 1, #c3 do
                        if c3[m].xarg["name"] == "interval" then
                            interval = c3[m].xarg["value"]
                        elseif c3[m].xarg["name"] == "sequence" then
                            sequence = c3[m].xarg["value"]
                            
                            -- find ids and add to tseq and convert to number
                            for w in string.gmatch(sequence, "[0-9]+") do
                                table.insert(tseq, tonumber(w))
                            end
                        end
                    end
                    
                    local i_id = tonumber(c2.xarg["id"])
                    tiles[i_id] = Tile.new(i_id, tseq, tonumber(interval))
                end
            end
        end
        
        -- parse layers
        if c1.label == "layer" then
            -- ignore Collision Layer
            if c1.xarg["name"] ~= "collision layer" then
                local layer_name = c1.xarg["name"]
                local ws = c1.xarg["width"]
                local wh = c1.xarg["height"]
                c2 = c1[1] -- <data>
                
                -- now iterate through tiles
                local data = {}
                for t = 1, #c2 do                    
                    table.insert(data, getTile(tiles, tonumber(c2[t].xarg["gid"])))
                end
                
                -- add layer to tilemap
                tilemap:addLayer(layer_name, tonumber(ws), tonumber(wh), data)
            end
        end
        
        -- parse objects
        if c1.label == "objectgroup" then            
            -- parse backgrounds/frontgrounds
            local bname = "back"
            local btype = "still"
            local bconid = 0
            local borderid = 0
            local bsource = ""
            local bx = 0
            local by = 0
            local bvel_x = 0
            local bvel_y = 0
            local brepeat_x = 0
            local brepeat_y = 0
            if c1.xarg["name"] == "backgrounds" then
                for b = 1, #c1 do
                    c2 = c1[b] -- <object>
                    bname = c2.xarg["name"]
                    btype = c2.xarg["type"]
                    
                    -- find container id, order id and source
                    c3 = c1[b][1] -- <properies>
                    for h = 1, #c3 do
                        if c3[h].xarg["name"] == "container" then
                            bconid = tonumber(c3[h].xarg["value"])
                        elseif c3[h].xarg["name"] == "order" then
                            borderid = tonumber(c3[h].xarg["value"])
                        elseif c3[h].xarg["name"] == "source" then
                            bsource = c3[h].xarg["value"]
                        elseif c3[h].xarg["name"] == "x" then
                            bx = c3[h].xarg["value"]
                        elseif c3[h].xarg["name"] == "y" then
                            by = c3[h].xarg["value"]
                        elseif c3[h].xarg["name"] == "vel_x" then
                            bvel_x = c3[h].xarg["value"]
                        elseif c3[h].xarg["name"] == "vel_y" then
                            bvel_y = c3[h].xarg["value"]
                        elseif c3[h].xarg["name"] == "repeat_x" then
                            brepeat_x = intToBoolean(tonumber(c3[h].xarg["value"]))
                        elseif c3[h].xarg["name"] == "repeat_y" then
                            brepeat_y = intToBoolean(tonumber(c3[h].xarg["value"]))
                        end
                    end
                    
                    if bname == "back" then
                        backgrounds[borderid] = Background.new(bsource, btype, bx, by, bvel_x, bvel_y, brepeat_x, brepeat_y, bconid)
                    elseif bname == "front" then
                        frontgrounds[borderid] = Background.new(bsource, btype, bx, by, bvel_x, bvel_y, brepeat_x, brepeat_y, bconid)
                    end
                end
            end
        
            -- parse containers
            if c1.xarg["name"] == "containers" then                
                for n = 1, #c1 do
                    c2 = c1[n]
                    containers[tonumber(c2.xarg["name"])] = ObjectContainer.new(tonumber(c2.xarg["name"]),
                                                                                tonumber(c2.xarg["x"]),
                                                                                tonumber(c2.xarg["y"]),
                                                                                tonumber(c2.xarg["width"]),
                                                                                tonumber(c2.xarg["height"]))
                end
            end
            
            -- parse spawns
            if c1.xarg["name"] == "spawns" then
                for j = 1, #c1 do
                    c2 = c1[j]
                    table.insert(spawns, Rectangle.new(tonumber(c2.xarg["x"]),
                                                      tonumber(c2.xarg["y"]),
                                                      tonumber(c2.xarg["width"]),
                                                      tonumber(c2.xarg["height"])))
                end
            end
            
            -- parse objects
            if c1.xarg["name"] == "objects" then
                for l = 1, #c1 do
                    c2 = c1[l]
                    -- player spawns
                    if c2.xarg["name"] == "player" then
                        ppos[tonumber(c2.xarg["type"])] = PlayerSpawn.new(tonumber(c2.xarg["x"]),
                                                                          tonumber(c2.xarg["y"]))
                    -- other objects like doors and stuff
                    else
                        local cod_id = 1
                        local cod_link_id = 1 -- only for doors
                        local obj = nil
                        
                        -- check container id
                        c3 = c2[1][1] -- <propery>
                        if c3 ~= nil and c3.xarg["name"] == "container" then
                            cod_id = tonumber(c3.xarg["value"])
                        end
                        
                        -- only for doors at the moment, check link id, door need to be in two container at the same
                        -- time
                        c3 = c2[1][2] -- <property>
                        if c2.xarg["name"] == "door" then
                            cod_link_id = tonumber(c3.xarg["value"])
                            -- create door
                            obj = objectLoader(c2.xarg["name"], 
                                               tonumber(c2.xarg["x"]),
                                               tonumber(c2.xarg["y"]),
                                               {cod_id, cod_link_id},
                                               c2.xarg["type"])
                                               
                            -- add to link container also
                            containers[cod_link_id]:addObject(obj)
                        -- parse enemies
                        elseif c2.xarg["name"] == "enemy" then
                            obj = Enemy.new(c2.xarg["type"],
                                            tonumber(c2.xarg["x"]),
                                            tonumber(c2.xarg["y"]),
                                            tonumber(c2.xarg["width"]),
                                            tonumber(c2.xarg["height"]))
                        -- parse other objects
                        else
                            obj = objectLoader(c2.xarg["name"], 
                                               tonumber(c2.xarg["x"]),
                                               tonumber(c2.xarg["x"]),
                                               tonumber(c2.xarg["width"]),
                                               tonumber(c2.xarg["height"]),
                                               c2.xarg["type"])
                        end
                    
                        -- add to container
                        containers[cod_id]:addObject(obj)
                    end
                end
            end
        end
    end
    
    return tilemap, backgrounds, frontgrounds, containers, spawns, ppos
end

-- helper for getting tile props
function getTile(tiles, id)
    if tiles[id] ~= nil then
        return Tile.new(tiles[id]["id"], tiles[id]["sequence"], tiles[id]["interval"])
    else
        return Tile.new(id)
    end
end

-- helper for parsing parameters (XML related)
function parseargs(s)
    local arg = {}
    string.gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
        arg[w] = a
    end)
    return arg
end
    
-- helper for parsing XML file.
function collect(s)
    local stack = {}
    local top = {}
    table.insert(stack, top)
    local ni,c,label,xarg, empty
    local i, j = 1, 1
    while true do
        ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i)
        if not ni then break end
        local text = string.sub(s, i, ni-1)
        if not string.find(text, "^%s*$") then
            table.insert(top, text)
        end
        if empty == "/" then  -- empty element tag
            table.insert(top, {label=label, xarg=parseargs(xarg), empty=1})
        elseif c == "" then   -- start tag
            top = {label=label, xarg=parseargs(xarg)}
            table.insert(stack, top)   -- new level
        else  -- end tag
            local toclose = table.remove(stack)  -- remove top
            top = stack[#stack]
            if #stack < 1 then
                error("nothing to close with "..label)
            end
            if toclose.label ~= label then
                error("trying to close "..toclose.label.." with "..label)
            end
            table.insert(top, toclose)
        end
        i = j+1
    end
    local text = string.sub(s, i)
    if not string.find(text, "^%s*$") then
        table.insert(stack[#stack], text)
    end
    if #stack > 1 then
        error("unclosed "..stack[stack.n].label)
    end
    return stack[1]
end